/*
 * INCLUDE FILES
 ****************************************************************************************
 */
#include <stdio.h>
#include <string.h>

#include "ble_stack.h"
#include "gap_api.h"
#include "gatt_api.h"
#include "gatt_sig_uuid.h"

#include "os_timer.h"
#include "os_mem.h"

#include "button.h"
#include "jump_table.h"
#include "co_printf.h"
#include "co_log.h"
#include "sys_utils.h"

#include "ota_service.h"
#include "dev_info_service.h"
#include "batt_service.h"
#include "hid_service.h"
#include "ble_hid_kbd_mice.h"

#include "plf.h"
#include "driver_system.h"
#include "driver_frspim.h"
#include "driver_pmu.h"
#include "driver_uart.h"

#undef LOG_LOCAL_LEVEL
#define LOG_LOCAL_LEVEL        (LOG_LEVEL_INFO)
const char *app_tag = "project";


#define SYSTEM_STACK_SIZE           0x800

uint8_t slave_link_conidx;
uint8_t master_link_conidx;
uint8_t tick = 1;

__attribute__((section("stack_section"))) static uint32_t system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)];

const struct jump_table_version_t _jump_table_version __attribute__((section("jump_table_3"))) =
{
    .stack_top_address = &system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)],
    .firmware_version = 0x00000000,
};

const struct jump_table_image_t _jump_table_image __attribute__((section("jump_table_1"))) =
{
    .image_type = IMAGE_TYPE_APP,
    .image_size = 0x20000,
};

os_timer_t update_timer;
void updata_tim_fn(void *arg)
{
    gap_conn_param_update(slave_link_conidx, 24, 24, 33, 600);
}

void proj_ble_gap_evt_func(gap_event_t *event)
{
    switch(event->type)
    {
        case GAP_EVT_ADV_END:
        {
            LOG_INFO(app_tag,"adv_end,status:0x%02x\r\n",event->param.adv_end.status);
        }
        break;
        case GAP_EVT_SCAN_END:
            LOG_INFO(app_tag,"scan_end,status:0x%02x\r\n",event->param.scan_end_status);
            break;
        case GAP_EVT_ADV_REPORT:
        {
            if(memcmp(event->param.adv_rpt->src_addr.addr.addr,"\x0C\x0C\x0C\x0C\x0C\x0B",6)==0)
            {
                co_printf("evt_type:0x%02x,rssi:%d\r\n",event->param.adv_rpt->evt_type,event->param.adv_rpt->rssi);

                co_printf("content:");
                show_reg(event->param.adv_rpt->data,event->param.adv_rpt->length,1);
            }
        }
        break;

        case GAP_EVT_ALL_SVC_ADDED:
        {
            LOG_INFO(app_tag,"all svc added\r\n");
        }
        break;

        case GAP_EVT_MASTER_CONNECT:
        {
            LOG_INFO(app_tag,"master[%d],connect. link_num:%d\r\n",event->param.master_connect.conidx,gap_get_connect_num());
				}
        break;

        case GAP_EVT_SLAVE_CONNECT:
        {
            LOG_INFO(app_tag,"slave[%d],connect. link_num:%d\r\n",event->param.slave_connect.conidx,gap_get_connect_num());
            show_reg(event->param.slave_connect.peer_addr.addr,6,1);
            slave_link_conidx = event->param.slave_connect.conidx;
            gatt_mtu_exchange_req(event->param.slave_connect.conidx);
            //if(!gap_security_get_bond_status())
            //gap_security_req(event->param.slave_connect.conidx);
        }
        break;

        case GAP_EVT_DISCONNECT:
        {
            LOG_INFO(app_tag,"Link[%d] disconnect,reason:0x%02X\r\n",event->param.disconnect.conidx
                     ,event->param.disconnect.reason);
            gap_start_advertising(0);
        }
        break;

        case GAP_EVT_LINK_PARAM_REJECT:
            LOG_INFO(app_tag,"Link[%d]param reject,status:0x%02x\r\n"
                     ,event->param.link_reject.conidx,event->param.link_reject.status);
            break;

        case GAP_EVT_LINK_PARAM_UPDATE:
            LOG_INFO(app_tag,"Link[%d]param update,interval:%d,latency:%d,timeout:%d\r\n",event->param.link_update.conidx
                     ,event->param.link_update.con_interval,event->param.link_update.con_latency,event->param.link_update.sup_to);
            break;

        case GAP_EVT_CONN_END:
            LOG_INFO(app_tag,"conn_end,reason:0x%02x\r\n",event->param.conn_end_reason);
            break;

        case GAP_EVT_PEER_FEATURE:
            LOG_INFO(app_tag,"peer[%d] feats ind\r\n",event->param.peer_feature.conidx);
            break;

        case GAP_EVT_MTU:
            LOG_INFO(app_tag,"mtu update,conidx=%d,mtu=%d\r\n"
                     ,event->param.mtu.conidx,event->param.mtu.value);
            break;
        case GAP_EVT_LINK_RSSI:
            LOG_INFO(app_tag,"link rssi %d\r\n",event->param.link_rssi);
            break;
        case GAP_SEC_EVT_MASTER_AUTH_REQ:
            LOG_INFO(app_tag,"link[%d],recv auth req:0x%02x\r\n",event->param.auth_req.conidx,event->param.auth_req.auth);
            break;
        case GAP_SEC_EVT_MASTER_ENCRYPT:
            LOG_INFO(app_tag,"master[%d]_encrypted\r\n",event->param.master_encrypt_conidx);
            break;
        case GAP_SEC_EVT_SLAVE_ENCRYPT:
            LOG_INFO(app_tag,"slave[%d]_encrypted\r\n",event->param.slave_encrypt_conidx);
            os_timer_init(&update_timer,updata_tim_fn,NULL);
            if(gap_security_get_bond_req())
                os_timer_start(&update_timer,6000,0);
            else
                os_timer_start(&update_timer,3000,0);
            break;

        default:
            break;
    }
}


__attribute__((section("ram_code"))) void user_entry_before_sleep_imp(void)
{
		pmu_calibration_stop();
    uart_putc_noint_no_wait(UART0, 's');
}

__attribute__((section("ram_code"))) void user_entry_after_sleep_imp(void)
{
    log_init();
    uart_putc_noint_no_wait(UART0, 'w');

    NVIC_EnableIRQ(PMU_IRQn);
		pmu_calibration_start(PMU_CALI_SEL_RCLFOSC, LP_RC_CALIB_CNT);
}

__attribute__((section("ram_code"))) void main_loop(void)
{
    while(1)
    {
        if(ble_stack_schedule_allow())
        {
            /*user code should be add here*/
            
            /* schedule internal stack event */
            ble_stack_schedule();
        }
        GLOBAL_INT_DISABLE();
        switch(ble_stack_sleep_check())
        {
            case 2:
            {
                ble_stack_enter_sleep();
                __jump_table.diag_port = 0x00008300;
                *(volatile uint32_t *)0x50000024 = 0xeeeeeeee;
            }
            break;
            default:
                break;
        }
        GLOBAL_INT_RESTORE();

				ble_stack_schedule_backward();
    }
}

void proj_init(void)
{
    LOG_INFO(app_tag,"proj_init\r\n");

    LOG_INFO(app_tag,"lp clk=%d\r\n", pmu_get_rc_clk(false));

    gap_dev_name_set("FR8010H", strlen("FR8010H"));

    //gap_set_dev_name("FR8010H", strlen("FR8010H"));
    /* register GAP callback function*/
    gap_set_cb_func(proj_ble_gap_evt_func);
		/* initialize SMP */
    //gap_bond_manager_init(BLE_BONDING_INFO_SAVE_ADDR, BLE_REMOTE_SERVICE_SAVE_ADDR, 8, true);
    gap_bond_manager_init(BLE_BONDING_INFO_SAVE_ADDR,BLE_REMOTE_SERVICE_SAVE_ADDR,8,true);
    
#if 1
    gap_security_param_t param =
    {
        .mitm = false,
        .ble_secure_conn = false,
        .io_cap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT,
        .pair_init_mode = GAP_PAIRING_MODE_WAIT_FOR_REQ,
        .bond_auth = true,
        .password = 0,
    };
#endif
    gap_security_param_init(&param);

    hid_start_adv();
    dis_gatt_add_service();
    batt_gatt_add_service();
    hid_gatt_add_service();
    ota_gatt_add_service();
}


void user_main(void)
{
    /* initialize log module */
    log_init();

    /* initialize PMU module at the beginning of this program */
    pmu_sub_init();
	  /* set local BLE address */
    mac_addr_t mac_addr;
    mac_addr.addr[0] = 0xbd;
    mac_addr.addr[1] = 0xad;
    mac_addr.addr[2] = 0x10;
    mac_addr.addr[3] = 0x11;
    mac_addr.addr[4] = 0x10;
    mac_addr.addr[5] = 0x20;
    gap_address_set(&mac_addr, BLE_ADDR_TYPE_PUBLIC);	//BLE_ADDR_TYPE_RANDOM_RESOVABLE
	
    /* configure ble stack capabilities */
    ble_stack_configure(BLE_STACK_ENABLE_MESH,
                        BLE_STACK_ENABLE_CONNECTIONS,
                        BLE_STACK_RX_BUFFER_CNT,
                        BLE_STACK_RX_BUFFER_SIZE, 
                        BLE_STACK_TX_BUFFER_CNT, 
                        BLE_STACK_TX_BUFFER_SIZE, 
                        BLE_STACK_ADV_BUFFER_SIZE,
                        BLE_STACK_RETENTION_RAM_SIZE,
                        BLE_STACK_KEY_STORAGE_OFFSET);	
    //jump_table_set_static_keys_store_offset(JUMP_TABLE_STATIC_KEY_OFFSET);
    /* initialize ble stack */
    ble_stack_init();

#if 1
    //system_sleep_disable();
		system_sleep_enable();
#else
    if(__jump_table.system_option & SYSTEM_OPTION_SLEEP_ENABLE)
    {
        co_printf("\r\na");
        co_delay_100us(10000);       //must keep it, or pressing reset key may block .
        co_printf("\r\nb");
        co_delay_100us(10000);
        co_printf("\r\nc");
        co_delay_100us(10000);
        co_printf("\r\nd");
    }
#endif
    proj_init();
    main_loop();
}

